home *** CD-ROM | disk | FTP | other *** search
- /*
- File: LaunchWithDoc2.c
-
- Contains: Document-launching sample program
- Loosely based on C.K. Haun's LaunchWithDoc
-
- This snippet includes these useful routines:
-
- OpenSpecifiedDocument
- finds the creator application for a document, whether or not
- the app is running, launches the app if necessary, and sends
- the Apple Event necessary to get the app to open the document
-
- FindApplicationFromDocument
- searches the mounted volumes for the application which
- created a document
-
- LaunchApplicationWithDocument
- launches an application which is not running and passes it
- the OpenDocuments event as part of the launch parameters
-
- SendOpenDocumentEventToProcess
- sends an OpenDocuments Apple event to a running program
-
- BuildOpenDocumentsEvent
- utility function to build an 'odoc' event from a list of
- FSSpecs.
-
- Remember that a target application need not be Apple event aware
- in order for the OpenDocuments event to succeed (the System will
- pull "puppet strings", simulating the events necessary to make the
- target app open the document)
-
- However, LaunchWithDoc must be high level event aware (as set in the
- SIZE resource) in order to send Apple events using AESend
-
- Written by: Greg Robbins
- modified by Nitin Ganatra
-
- Copyright: Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 7/27/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
-
- #include <QuickDraw.h>
- #include <StandardFile.h>
- #include <Fonts.h>
- #include <Menus.h>
- #include <Dialogs.h>
- #include <Events.h>
- #include <Files.h>
- #include <TextEdit.h>
- #include <Memory.h>
- #include <Errors.h>
- #include <Processes.h>
- #include <AppleEvents.h>
- #include <Aliases.h>
-
- // prototypes
-
- OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr);
- OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
- FSSpecPtr applicationFSSpecPtr);
- void ReportError(StringPtr messageStr);
-
- // what functions changed since LaunchWithDoc2
-
- OSErr BuildOpenDocumentEvent(ProcessSerialNumber *targetPSN,
- const FSSpec *theSpecArray, const short numOfSpecs, AppleEvent *odocAppleEvent);
- OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN,
- const FSSpec *theSpecArray, const short numOfSpecs);
- OSErr LaunchApplicationWithDocument(const FSSpec *applicationFSSpecPtr,
- const FSSpec *theSpecArray, const short numOfSpecs);
-
-
- // main program
- //
- // the main routine raises a std file dialog to let the
- // user choose a document and then opens the document
- // in the appropriate application
-
- void main(void)
- {
- OSErr retCode;
- StandardFileReply documentStdFileReply;
- SFTypeList mySFTypeList;
-
- // initialize the toolbox
- InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus();
- TEInit(); InitDialogs(nil); InitCursor();
-
- // Simplest case: get a document and open it
- StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
- if (documentStdFileReply.sfGood) {
-
- retCode = OpenSpecifiedDocument(&documentStdFileReply.sfFile);
- if (retCode != noErr) ReportError("\p OpenDocument failed");
- }
-
- /*
- // Another case: get an application to launch and three documents
- // to open on startup
- StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
- if (documentStdFileReply.sfGood) {
-
- appSpec = documentStdFileReply.sfFile;
- for (index = 0; index < 3; index ++) {
- StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
- if (documentStdFileReply.sfGood)
- docTmpSpec[index] = documentStdFileReply.sfFile;
-
- }
-
- retCode = LaunchApplicationWithDocument(&appSpec, docTmpSpec, index);
- }
- */
-
- }
-
- void ReportError(StringPtr messageStr)
- {
- DebugStr(messageStr);
- }
-
-
- // OpenSpecifiedDocument searches to see if the application which
- // created the document is already running. If so, it sends
- // an OpenSpecifiedDocuments Apple event to the target application
- // (remember that, because of puppet strings, this works even
- // if the target application is not Apple event-aware.)
-
- OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr)
- {
- OSErr retCode;
- ProcessSerialNumber currPSN;
- ProcessInfoRec currProcessInfo;
- FSSpec applicationSpec;
- FInfo documentFInfo;
- Boolean foundRunningProcessFlag;
-
- // verify the document file exists and get its creator type
-
- retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
- if (retCode != noErr) goto Bail;
-
- // check the current processes to see if the creator app is already
- // running, and get its process serial number (as currPSN)
-
- currPSN.lowLongOfPSN = kNoProcess;
- currPSN.highLongOfPSN = 0;
-
- currProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
- currProcessInfo.processName = nil;
- currProcessInfo.processAppSpec = &applicationSpec;
-
- foundRunningProcessFlag = false;
- while (GetNextProcess(&currPSN) == noErr) {
- if (GetProcessInformation(&currPSN, &currProcessInfo) == noErr) {
- if (currProcessInfo.processSignature == documentFInfo.fdCreator) {
- foundRunningProcessFlag = true;
- break;
- }
- }
- }
-
- // if the creator is running, send it an OpenDocuments Apple event
- // since there is no need to launch it
-
- if (foundRunningProcessFlag)
- retCode = SendOpenDocumentEventToProcess(&currPSN, documentFSSpecPtr, 1);
-
- // else if the creator is not running, find it on disk and launch
- // it with the OpenDocuments event included as a part of the
- // launch parameters
-
- else {
- retCode = FindApplicationFromDocument(documentFSSpecPtr, &applicationSpec);
-
- if (retCode == noErr)
-
- retCode = LaunchApplicationWithDocument(&applicationSpec,
- documentFSSpecPtr, 1);
- }
-
- Bail:
- return retCode;
- }
-
-
- //----------------------------------------------------------------------------
- // LaunchApplicationWithDocument
- //
- // given an application and any number of documents,
- // LaunchApplicationWithDocument launches the application and passes the
- // application an OpenDocuments event for the document(s)
- //----------------------------------------------------------------------------
- OSErr LaunchApplicationWithDocument(
- const FSSpec *applicationFSSpecPtr,
- const FSSpec *theSpecArray,
- const short numOfSpecs)
- {
- OSErr retCode;
- LaunchParamBlockRec launchParams;
- AppleEvent theAppleEvent;
- AEDesc launchParamDesc;
- ProcessSerialNumber targetPSN;
-
- // to simplify cleanup, ensure that handles are nil to start
- launchParams.launchAppParameters = nil;
- theAppleEvent.dataHandle = nil;
- launchParamDesc.dataHandle = nil;
-
- if (theSpecArray != nil) {
-
- // because 'odoc' events require a address descriptor, I just
- // grab the PSN for the current process. It doesn't matter what
- // it is, because it's never used.
- (void) GetCurrentProcess(&targetPSN);
-
- // build an 'odoc' event given the array of FSSpecs and the ProcessSerialNumber
- retCode = BuildOpenDocumentEvent(&targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
-
- if (retCode == noErr) {
-
- // coerce the AppleEvent into app parameters, for _LaunchApplication
- retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc);
- if (retCode != noErr) goto Bail;
-
- // fill in the launch parameter block, including the
- // Apple event, and make the launch call
- HLock((Handle) launchParamDesc.dataHandle);
- launchParams.launchAppParameters =
- (AppParametersPtr) *(launchParamDesc.dataHandle);
-
- }
-
- }
-
- launchParams.launchBlockID = extendedBlock;
- launchParams.launchEPBLength = extendedBlockLen;
- launchParams.launchFileFlags = launchNoFileFlags;
- launchParams.launchControlFlags = launchContinue;
- launchParams.launchAppSpec = (FSSpecPtr)applicationFSSpecPtr;
-
- retCode = LaunchApplication(&launchParams);
-
- Bail:
- // dispose of everything that was allocated
- if (theAppleEvent.dataHandle != nil) (void) AEDisposeDesc(&theAppleEvent);
- if (launchParamDesc.dataHandle != nil) (void) AEDisposeDesc(&launchParamDesc);
-
- return retCode;
-
- }
-
-
- //----------------------------------------------------------------------------
- // SendOpenDocumentEventToProcess
- //
- // given an application's serial number and any number of documents,
- // SendOpenDocumentEventToProcess passes
- // the application an OpenDocuments event for the document
- //----------------------------------------------------------------------------
- OSErr SendOpenDocumentEventToProcess(
- ProcessSerialNumber *targetPSN,
- const FSSpec *theSpecArray,
- const short numOfSpecs)
- {
- OSErr retCode;
- AppleEvent theAppleEvent, theReplyEvent;
-
- theAppleEvent.dataHandle = nil;
- retCode = BuildOpenDocumentEvent(targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
-
- if (retCode == noErr)
- retCode = AESend(&theAppleEvent,
- &theReplyEvent,
- kAENoReply,
- kAENormalPriority,
- kAEDefaultTimeout,
- nil,
- nil);
-
- // dispose of the AppleEvent if it was allocated
- if (theAppleEvent.dataHandle != nil)
- (void) AEDisposeDesc(&theAppleEvent);
-
- return retCode;
-
- }
-
-
- // FindApplicationFromDocument uses the Desktop Database to
- // locate the creator application for the given document
- //
- // this routine will first check the desktop database of the disk
- // containing the document, then the desktop database of all local
- // disks, then the desktop databases of all server volumes
- // (so up to three passes will be made)
-
- OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
- FSSpecPtr applicationFSSpecPtr)
- {
- enum { documentPass, localPass, remotePass, donePass } volumePass;
- DTPBRec desktopParams;
- HParamBlockRec hfsParams;
- FInfo documentFInfo;
- short volumeIndex;
- Boolean foundFlag;
- GetVolParmsInfoBuffer volumeInfoBuffer;
- OSErr retCode;
-
- // dkj 12/94 initialize flag to false (thanks to Peter Baral for pointing out this bug)
- foundFlag = false;
-
- // verify the document file exists and get its creator type
-
- retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
- if (retCode != noErr) goto Bail;
-
- volumePass = documentPass;
- volumeIndex = 0;
-
- do {
-
- // first, find the vRefNum of the volume whose Desktop Database
- // we're checking this time
-
- // if we're on the initial pass (documentPass) just use
- // the vRefNum of the document itself
-
- if (volumePass == documentPass)
-
- desktopParams.ioVRefNum = documentFSSpecPtr->vRefNum;
-
- // otherwise, find the vRefNum of the next volume appropriate
- // for this pass
-
- else {
-
- volumeIndex++;
-
- // convert the volumeIndex into a vRefNum
-
- hfsParams.volumeParam.ioNamePtr = nil;
- hfsParams.volumeParam.ioVRefNum = 0;
- hfsParams.volumeParam.ioVolIndex = volumeIndex;
- retCode = PBHGetVInfoSync(&hfsParams);
-
- // a nsvErr indicates that the current pass is over
- if (retCode == nsvErr) goto SkipThisVolume;
- if (retCode != noErr) goto Bail;
-
- // since we handled the document volume during the documentPass,
- // skip it if we have hit that volume again
-
- if (hfsParams.volumeParam.ioVRefNum == documentFSSpecPtr->vRefNum)
- goto SkipThisVolume;
-
- // call GetVolParms to determine if this volume is a server
- // (a remote volume)
-
- hfsParams.ioParam.ioBuffer = (Ptr) &volumeInfoBuffer;
- hfsParams.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
- retCode = PBHGetVolParmsSync(&hfsParams);
- if (retCode != noErr) goto Bail;
-
- // if the vMServerAdr field of the volume information buffer
- // is zero, this is a local volume; skip this volume
- // if it's local on a remote pass or remote on a local pass
-
- if ((volumeInfoBuffer.vMServerAdr != 0) !=
- (volumePass == remotePass)) goto SkipThisVolume;
-
- // okay, now we've found the vRefNum for our desktop database call
-
- desktopParams.ioVRefNum = hfsParams.volumeParam.ioVRefNum;
- }
-
- // find the path refNum for the desktop database for
- // the volume we're interested in
-
- desktopParams.ioNamePtr = nil;
-
- retCode = PBDTGetPath(&desktopParams);
- if (retCode == noErr && desktopParams.ioDTRefNum != 0) {
-
- // use the GetAPPL call to find the preferred application
- // for opening any document with this one's creator
-
- desktopParams.ioIndex = 0;
- desktopParams.ioFileCreator = documentFInfo.fdCreator;
- desktopParams.ioNamePtr = applicationFSSpecPtr->name;
- retCode = PBDTGetAPPLSync(&desktopParams);
-
- if (retCode == noErr) {
-
- // okay, found it; fill in the application file spec
- // and set the flag indicating we're done
-
- applicationFSSpecPtr->parID = desktopParams.ioAPPLParID;
- applicationFSSpecPtr->vRefNum = desktopParams.ioVRefNum;
- foundFlag = true;
-
- }
- }
-
- SkipThisVolume:
-
- // if retCode indicates a no such volume error or if this
- // was the first pass, it's time to move on to the next pass
-
- if (retCode == nsvErr || volumePass == documentPass) {
- volumePass++;
- volumeIndex = 0;
- }
-
- } while (foundFlag == false && volumePass != donePass);
-
- Bail:
- return retCode;
- }
-
-
- //----------------------------------------------------------------------------
- // BuildOpenDocumentsEvent
- //
- // General utility to turn a ProcessSerialNumber and a list of FSSpecs into
- // an 'odoc' AppleEvent with the ProcessSerialNumber as the target
- // application. Used by SendOpenDocumentEventToProcess, and
- // LaunchApplicationWithDocument.
- //----------------------------------------------------------------------------
- OSErr BuildOpenDocumentEvent(
- ProcessSerialNumber *targetPSN,
- const FSSpec *theSpecArray,
- const short numOfSpecs,
- AppleEvent *odocAppleEvent)
- {
- OSErr retCode;
- AppleEvent theAppleEvent;
- AEDesc targetAddrDesc, docDesc;
- AEDescList docDescList;
- AliasHandle docAlias;
- short counter;
- FSSpecPtr specIterator;
-
- // to simplify cleanup, ensure that handles are nil to start
- targetAddrDesc.dataHandle = nil;
- theAppleEvent.dataHandle = nil;
- docDescList.dataHandle = nil;
- docDesc.dataHandle = nil;
- docAlias = nil;
-
- // create an address descriptor based on the serial number of
- // the target process
- retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN,
- sizeof(ProcessSerialNumber), &targetAddrDesc);
- if (retCode != noErr) goto Bail;
-
- // make a descriptor list containing just a descriptor with an
- // alias to the document
- retCode = AECreateList(nil, 0, false, &docDescList);
- if (retCode != noErr) goto Bail;
-
- // start at the beginning of the FSSpec list, and start adding
- // them to the document list descriptor
-
- // NOTE: we need to make sure we dispose of the aliases and the
- // AE descriptor in this loop, otherwise there will be memory
- // leaks.
- specIterator = (FSSpecPtr)theSpecArray;
- for (counter = 0; counter < numOfSpecs; counter ++) {
-
- retCode = NewAlias(nil, &specIterator[counter], &docAlias);
- if (retCode != noErr) goto Bail;
-
- HLock((Handle) docAlias);
- retCode = AECreateDesc(typeAlias, (Ptr) *docAlias,
- InlineGetHandleSize((Handle) docAlias), &docDesc);
- HUnlock((Handle) docAlias);
-
- if (retCode != noErr) goto Bail;
- // the alias is now in the AEDescriptor, so dispose of it
- DisposeHandle((Handle)docAlias); docAlias = nil;
-
- retCode = AEPutDesc(&docDescList, 0, &docDesc);
- if (retCode != noErr) goto Bail;
-
- // the alias is now in the AE document list, so dispose of it
- retCode = AEDisposeDesc(&docDesc);
- if (retCode != noErr) goto Bail;
-
- }
-
- // now make the 'odoc' AppleEvent descriptor and insert the
- // document descriptor list as the direct object
- retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
- &targetAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
- &theAppleEvent);
- if (retCode != noErr) goto Bail;
-
- retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
- if (retCode != noErr) goto Bail;
-
- *odocAppleEvent = theAppleEvent;
-
- Bail:
- // dispose of everything that was allocated, except the return AE
- if (targetAddrDesc.dataHandle != nil) (void) AEDisposeDesc(&targetAddrDesc);
- if (docDescList.dataHandle != nil) (void) AEDisposeDesc(&docDescList);
- if (docDesc.dataHandle != nil) (void) AEDisposeDesc(&docDesc);
- if (docAlias != nil) DisposeHandle((Handle) docAlias);
-
- return retCode;
-
- }
-